use salvo::{
    oapi::{
        endpoint,
        extract::{JsonBody, PathParam, QueryParam},
    },
    Depot, Request,
};
use tracing::warn;
use validator::Validate;

use crate::{
    app_writer::{AppResult, AppWriter}, dtos::paper::{PaperAddRequest, PaperResponse, PaperUpdateRequest}, entities::papers::Paper, search_engine::get_search_engine_paper, services::paper::{self, own_paper}
};
use salvo::Writer;
use crate::app_error::AppError;
use crate::search_engine::SearchEngine;

use super::get_userid_from_jwt;

#[endpoint(tags("papers"))]
pub async fn post_add_paper(
    new_paper: JsonBody<PaperAddRequest>,
    depot: &mut Depot,
) -> AppResult<AppWriter<PaperResponse>> {
    let user_id = get_userid_from_jwt(depot)?;
    new_paper.0.validate()?;
    let res = paper::add_paper(new_paper.0, user_id).await?;

    let search_engine = get_search_engine_paper().await?;
    let paper = Paper::from(res.clone());
    // 异步添加文档，防止阻塞；
    let _ = tokio::spawn(async {
        if let Err(e) = async {
            search_engine.add_paper(paper).await?;
            let _ = search_engine.commit().await?;
            Ok::<(), AppError>(())
        }.await {
            tracing::error!("add paper to search engine failed: {}", e)
        }
    });
    let res = Ok(res);
    Ok(AppWriter(res))
}

#[endpoint(  tags("papers"),
parameters(
    ("id", description = "paper id"),
))]
pub async fn put_update_paper(
    req: &mut Request,
    depot: &mut Depot,
) -> AppResult<AppWriter<PaperResponse>> {
    let user_id = get_userid_from_jwt(depot)?;
    let req: PaperUpdateRequest = req.extract().await?;
    let res = paper::update_paper(req, user_id).await?;

    let search_engine = get_search_engine_paper().await?;
    let paper = Paper::from(res.clone());
    let _ = tokio::spawn(async {
        if let Err(e) = async {
            search_engine.update_paper(paper).await?;
            let _ = search_engine.commit().await?;
            Ok::<(), AppError>(())
        }.await {
            warn!("add paper to search engine failed: {}", e)
        }
    });

    let res = Ok(res);
    Ok(AppWriter(res))
}

#[endpoint(tags("papers"))]
pub async fn get_papers(depot: &mut Depot) -> AppResult<AppWriter<Vec<PaperResponse>>> {
    let user_id = get_userid_from_jwt(depot)?;
    let res = paper::papers(user_id).await?;
    let res = Ok(res);
    Ok(AppWriter(res))
}

#[endpoint(tags("papers"),
parameters(
    ("id", description = "paper id"),
))]
pub async fn delete_paper(
    id: PathParam<String>,
    depot: &mut Depot,
) -> AppResult<AppWriter<()>> {
    let user_id = get_userid_from_jwt(depot)?;
    let paper_id = id.as_str();
    let _ = paper::dis_own_paper(user_id, paper_id).await?;
    Ok(AppWriter(Ok(())))
}

#[endpoint(tags("papers"))]
pub async fn get_share_paper(paper_id: QueryParam<String>, depot: &mut Depot) -> AppResult<AppWriter<PaperResponse>> {
    let user_id = get_userid_from_jwt(depot)?;
    let _ = own_paper(user_id, &paper_id).await?;
    let res = paper::paper(&paper_id).await?;
    let res = AppWriter(Ok(res));
    Ok(res)
}